#ifndef __COMMON_FUTURE_HH__
#define __COMMON_FUTURE_HH__

#include <set>
#include <vector>
#include <boost/bind.hpp>
#include <boost/bind/placeholders.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

namespace Common
{

class AbstractFuture : public boost::enable_shared_from_this< AbstractFuture >
{
	public: // typedefs
		typedef boost::shared_ptr< AbstractFuture > Ptr;
		typedef boost::function<void (const AbstractFuture::Ptr &)> Observer;

		typedef enum { NONE, SUCCESS, FAILURE } Status;

	private: // attributes
		std::vector< Observer > successObservers;
		std::vector< Observer > failureObservers;

	protected: // attributes
		Status status;

		void callObservers()
		{
			if ( status == SUCCESS )
			{
				BOOST_FOREACH( Observer &observer, successObservers )
				{
					observer( shared_from_this() );
				}
			}
		   	else if ( status == FAILURE )
		   	{
				BOOST_FOREACH( Observer &observer, failureObservers )
				{
					observer( shared_from_this() );
				}
			}
			else
			{
				throw std::logic_error( "Observers can be called only when Future is completed with value!" );
			}
		}
	
	public: // methods
		AbstractFuture() : status(NONE) {};

		virtual ~AbstractFuture() {};

		void addSuccessObserver( const Observer &aObserver ) { successObservers.push_back( aObserver ); }
		void addFailureObserver( const Observer &aObserver ) { failureObservers.push_back( aObserver ); }

		Status getStatus() const { return status; }
};

template< typename Result, typename Error >
class Future : public AbstractFuture
{
	public: // typedefs
		typedef boost::shared_ptr< Future > Ptr;

		typedef typename Result::Ptr ResultPtr;
		typedef typename Error::Ptr ErrorPtr;

	protected: // attributes
		ResultPtr result;
		ErrorPtr error;

	public: // methods
		void success( const ResultPtr &aResult ) { status = SUCCESS; result = aResult; callObservers(); }
		void failure( const ErrorPtr &aError ) { status = FAILURE; error = aError; callObservers(); }
	
		const ResultPtr& getResult() const { assert( status == SUCCESS); return result; }
		const ErrorPtr& getError() const { assert( status == FAILURE); return error; }
};

class FutureList : public AbstractFuture
{
	public: // typedefs
		typedef boost::shared_ptr< FutureList > Ptr;
	
	private: // attributes
		std::set< AbstractFuture::Ptr > futures;
		std::set< AbstractFuture::Ptr > completedFutures;

		struct CompletedFutureIsFailed : public std::unary_function< bool, const AbstractFuture::Ptr & >
		{
			bool operator()( const AbstractFuture::Ptr &aFuture )
			{
				return ( aFuture->getStatus() == AbstractFuture::FAILURE );
			}
		};

		void handleFuture( const AbstractFuture::Ptr &aFuture )
		{
			completedFutures.insert( aFuture );

			if ( completedFutures == futures )
			{
				if ( std::count_if( futures.begin(), futures.end(), CompletedFutureIsFailed() ) == 0 )
					status = AbstractFuture::SUCCESS;
				else
					status = AbstractFuture::FAILURE;

				callObservers();
			}
		}
	
	public: // methods
		void addFuture( const AbstractFuture::Ptr &aFuture )
	   	{
		   	futures.insert( aFuture ); 

			aFuture->addSuccessObserver( boost::bind( &FutureList::handleFuture, this, _1 ) );
			aFuture->addFailureObserver( boost::bind( &FutureList::handleFuture, this, _1 ) );
		}
};

};

#endif
